/*  Pattern based natural language processing tools for Estonian.
    Copyright (C) 2013 University of Tartu

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <Corpus.hpp>

#include <fstream>
#include <sstream>

namespace patnlp
{

////////////////////////////////////////////////////////////////////////////////
// Corpus cover related
////////////////////////////////////////////////////////////////////////////////

OrderedDocCover full_ordered_doc_cover_from_doc(Document const& doc)
{
    LongVector indices;
    indices.reserve(doc.size());
    for (unsigned long i=0 ; i<doc.size() ; ++i) {
        indices.push_back(i);
    }
    return OrderedDocCover(doc.size(), indices);
}

OrderedCover full_ordered_cover_from_corpus(Corpus const& corpus)
{
    std::map<std::string, OrderedDocCover> map;
    for (auto i=corpus.begin() ; i!=corpus.end() ; ++i) {
        map.insert(map.end(), {i->first, full_ordered_doc_cover_from_doc(i->second)});
    }
    return OrderedCover(map);
}

////////////////////////////////////////////////////////////////////////////////
// Corpus parsing
////////////////////////////////////////////////////////////////////////////////

Document read_doc_from_stream(std::istream& is, std::string& doc_name)
{
    Document document;
    StringVector word;

    std::string line;
    std::string token;

    std::getline(is, doc_name);
    std::getline(is, line);
    while (!is.eof() && !is.fail()) {
        if (line.size() == 0) {
            break;
        }
        std::stringstream ls(line);
        std::getline(ls, token, '\t');
        word.push_back(token);
        while (!ls.eof()) {
            std::getline(ls, token, '\t');
            word.push_back(token);
        }
        document.push_back(word);
        word.clear();

        std::getline(is, line);
    }
    return document;
}

Corpus read_corpus_from_stream(std::istream& is)
{
    Corpus corpus;
    std::string doc_name;
    Document doc;

    doc = read_doc_from_stream(is, doc_name);
    while (!is.eof() && !is.fail()) {
        corpus[doc_name] = doc;
        doc = read_doc_from_stream(is, doc_name);
    }
    if (corpus.find(doc_name) == corpus.end()) {
        corpus[doc_name] = doc;
    }
    return corpus;
}

Document read_doc_from_str(std::string const& s, std::string& doc_name)
{
    std::stringstream ss(s);
    return read_doc_from_stream(ss, doc_name);
}

Document read_doc_from_file(std::string const& filename, std::string& doc_name)
throw (std::runtime_error)
{
    std::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary);
    if (fin.fail()) {
        std::string err = ERR_COULD_NOT_READ_FILE + filename;
        throw std::runtime_error(err.c_str());
    }
    Document doc = read_doc_from_stream(fin, doc_name);
    fin.close();
    return doc;
}

Corpus read_corpus_from_str(std::string const& s)
{
    std::stringstream ss(s);
    return read_corpus_from_stream(ss);
}

Corpus read_corpus_from_file(std::string const& filename)
throw (std::runtime_error)
{
    std::ifstream fin(filename, std::ios::in | std::ios::binary);
    if (fin.fail()) {
        std::string err = ERR_COULD_NOT_READ_FILE + filename;
        throw std::runtime_error(err.c_str());
    }
    Corpus corpus = read_corpus_from_stream(fin);
    fin.close();
    return corpus;
}

////////////////////////////////////////////////////////////////////////////////
// Corpus writing
////////////////////////////////////////////////////////////////////////////////

void write_doc_to_stream(std::ostream& os, Document const& doc, std::string const& doc_name)
{
    for (auto i=doc.begin() ; i!=doc.end() ; ++i) {
        for (auto j=i->begin() ; j<i->end() ; ++j)     {
            if (j != i->begin()) {
                os << '\t';
            }
            os << *j;
        }
        os << std::endl;
    }
}

void write_corpus_to_stream(std::ostream& os, Corpus const& corpus)
{
    for (auto i=corpus.begin() ; i!=corpus.end() ; ++i) {
        if (i!=corpus.begin()) {
            os << std::endl;
        }
        os << i->first << std::endl;
        write_doc_to_stream(os, i->second, i->first);
    }
}

void write_corpus_to_file(std::string const& filename, Corpus const& corpus)
throw (std::runtime_error)
{
    std::ofstream fout(filename.c_str(), std::ios::binary | std::ios::out);
    if (!fout) {
        fout.close();
        throw std::runtime_error((ERR_COULD_NOT_WRITE_FILE + filename).c_str());
    }
    write_corpus_to_stream(fout, corpus);
    fout.close();
}

Corpus corpus_sample(Corpus const& corpus, std::vector<std::string> const& doc_ids)
throw(std::runtime_error)
{
    Corpus sample;
    for (auto i=doc_ids.begin() ; i!=doc_ids.end() ; ++i) {
        auto j = corpus.find(*i);
        if (j == corpus.end()) {
            throw std::runtime_error(ERR_DOCUMENT_NOT_FOUND);
        } else {
            sample[*i] = j->second;
        }
    }
    return sample;
}

} // namespace patnlp
